package jmine.tec.hydra.domain.actions;

import static jmine.tec.hydra.api.MessageDirection.SENT;
import static jmine.tec.hydra.domain.filter.HydraFilterType.SENDING;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

import jmine.tec.hydra.api.EncodeMessageAction;
import jmine.tec.hydra.api.HydraAction;
import jmine.tec.hydra.api.HydraMessage;
import jmine.tec.hydra.api.SendMessageAction;
import jmine.tec.hydra.codec.EncoderData;
import jmine.tec.hydra.domain.filter.HydraFilterService;
import jmine.tec.persist.annotation.DiscriminatorComment;
import jmine.tec.script.ScriptLogger;

import org.apache.commons.lang.StringUtils;

/**
 * Action responsável por codificar uma mensagem de acordo com uma especificação.
 * 
 * @author lundberg
 */
@Entity
@DiscriminatorValue("5")
@DiscriminatorComment("CODIFICACAO DE MENSAGEM")
public class DefaultEncodeMessageAction extends AbstractHydraAction implements EncodeMessageAction {

    // Atributos transientes, existem apenas na criação da action e deixam de existir uma vez que a mensagem foi armazenada com sucesso.
    private Object source;

    /**
     * {@inheritDoc}
     */
    public EncodeMessageAction prepare(Object src) {
        this.source = src;
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected List<HydraAction> doExecute() throws Exception {
        LOGGER.debug(String.format("Criando mensagem para o sistema %s.", this.getRequest().getSystem().getMnemonico()));
        EncoderData encoderData = this.encode();
        List<ScriptLogger> loggers = encoderData.getLoggers();
        this.processLogs(loggers);
        HydraMessage message = this.createMessage(encoderData);
        LOGGER.debug(String.format("Mensagem criada (id=%s) e associada com a conversa (%s).", message.getId(), message.getConversation()
                .getMnemonico()));
        this.logAsSystem(String.format("Mensagem ao sistema %s codificada com sucesso.", this.getRequest().getSystem().getMnemonico()));
        if (this.getRequest().getSpecification().hasSender()) {
            SendMessageAction action = this.getController().getProvider().createAction(SendMessageAction.class);
            action.prepare(message);
            return Collections.singletonList((HydraAction) action);
        } else {
            return Collections.emptyList();
        }
    }

    /**
     * Processa os logs.
     * 
     * @param loggers os loggers
     */
    private void processLogs(List<ScriptLogger> loggers) {
        boolean allSuccess = true;
        for (ScriptLogger logger : loggers) {
            if (logger == null) {
                continue;
            }
            if (!logger.isSuccess()) {
                allSuccess = false;
            }
            this.logAsUser(logger);
        }

        if (allSuccess) {
            LOGGER.debug(String.format("Codificação obteve sucesso.", this.getRequest().getSystem().getMnemonico()));
        } else {
            this.markAsPending();
            LOGGER.debug(String.format("Codificação pendente.", this.getRequest().getSystem().getMnemonico()));
        }
    }

    /**
     * Loga as mensagens obtidas.
     * 
     * @param logger o logger
     */
    private void logAsUser(ScriptLogger logger) {
        for (String log : logger.getLogs()) {
            this.logAsUser(log);
        }
        if (!StringUtils.isBlank(logger.getTraces())) {
            this.logAsUser(logger.getTraces());
        }
    }

    /**
     * Executa a codificação da mensagem de acordo com a especificação
     * 
     * @return EncoderData
     */
    private EncoderData encode() {
        return this.getRequest().getSpecification().getEncoder().encode(this.source);
    }

    /**
     * Cria uma instância de {@link HydraMessage} a partir da fonte informada, utilizando a especificação dada.
     * 
     * @param encoderData encoderData
     * @return mensagem criada.
     * @throws Exception Exception
     */
    private HydraMessage createMessage(EncoderData encoderData) throws Exception {
        HydraMessage message =
                this.getController().getProvider()
                        .createMessage(this.getRequest(), SENT, encoderData.getMessageStream(), new HashMap<String, String>());
        new HydraFilterService(message, this.getRequest().getSpecification(), SENDING).applyFilters();
        this.saveMessageWithConversation(encoderData.getIdentification(), message);
        return message;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String createDescription() {
        return "Codificação de mensagem";
    }
}
